package com.example.demo.config.datasource;

import com.alibaba.druid.filter.config.ConfigTools;
import com.alibaba.druid.pool.DruidDataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.StringTokenizer;

/**
 * @author: wuhong-016
 * @time: 2021/3/5 9:51
 * @version: v1.0.0
 * @description:动态数据源配置
 */
@Configuration
public class DataSourceConfig {

    private static Logger logger = LoggerFactory.getLogger(DataSourceConfig.class);


    private static final String DEFAULT_PREFIX = "spring.datasource.druid.";

    public static final String DS1 = "ds1";
    public static final String DS2 = "ds2";
    public static final String DS3 = "ds3";
    public static final String DEFAULT = DS3;

    /**
     * 按顺序初始化
     * 当后面的数据源属性为空时
     * 使用默认数据源相关属性
     */
    private static Map<String, String> DATASOURCE_MAP = new LinkedHashMap<>();

    static {
        DATASOURCE_MAP.put(DEFAULT, DEFAULT_PREFIX + DEFAULT + ".%s");
        DATASOURCE_MAP.put(DS1, DEFAULT_PREFIX + DS1 + ".%s");
        DATASOURCE_MAP.put(DS2, DEFAULT_PREFIX + DS2 + ".%s");
        DATASOURCE_MAP.put(DS3, DEFAULT_PREFIX + DS3 + ".%s");
    }


    @Resource
    Environment environment;

    @Bean
    public DataSource dataSource() throws Exception {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();

        Map<Object, Object> dataSourceMap = getDataSourceMap();

        dynamicDataSource.setDefaultTargetDataSource(dataSourceMap.get(DEFAULT));
        dynamicDataSource.setTargetDataSources(dataSourceMap);

        return dynamicDataSource;
    }

    private Map<Object, Object> getDataSourceMap() throws Exception {
        Map<Object, Object> dataSourceMap = new HashMap<>();

        DruidDataSource druidDataSource = new DruidDataSource();

        for (String key : DATASOURCE_MAP.keySet()) {
            String p = environment.getProperty(String.format(DATASOURCE_MAP.get(key), "password"));
            Boolean enable = environment.getProperty(String.format(DATASOURCE_MAP.get(key), "decrypt.enable"), Boolean.class);
            if (enable) {
                String publicKey = environment.getProperty(String.format(DATASOURCE_MAP.get(key), "decrypt.publicKey"));
                p = this.decrypt(p, publicKey);
            }

            String driverClassName = environment.getProperty(String.format(DATASOURCE_MAP.get(key), "driverClassName"));
            if (null == driverClassName || "".equals(driverClassName)) {
                driverClassName = environment.getProperty(String.format(DATASOURCE_MAP.get(key), "driver-class-name"));
            }

            druidDataSource.setName(environment.getProperty(String.format(DATASOURCE_MAP.get(key), "name")));


            druidDataSource.setDriverClassName(driverClassName);

            druidDataSource.setUrl(environment.getProperty(String.format(DATASOURCE_MAP.get(key), "url")));
            druidDataSource.setUsername(environment.getProperty(String.format(DATASOURCE_MAP.get(key), "username")));
            druidDataSource.setPassword(p);


            this.configFromPropety(DATASOURCE_MAP.get(key), druidDataSource);

            logger.info("数据源{}构建成功", key);

            dataSourceMap.put(key, druidDataSource);
        }

        return dataSourceMap;
    }

    private String decrypt(String pwd, String publicKey) {
        try {
            logger.info(">>> 数据库密码解密,使用默认秘钥");
            return ConfigTools.decrypt(pwd);
        } catch (Exception e) {
            try {
                if (!StringUtils.isEmpty(publicKey)) {
                    logger.info(">>> 数据库密码解密,默认秘钥解密失败，使用配置秘钥");
                    return ConfigTools.decrypt(publicKey, pwd);
                }
            } catch (Exception e1) {
                logger.info(">>> 数据库密码解密,配置秘钥解密失败");
            }
        }
        logger.info(">>> 数据库密码，使用原文");
        return pwd;

    }


    private void configFromPropety(String prefix, DruidDataSource dataSource) {

        Boolean value = environment.getProperty(String.format(prefix, "testWhileIdle"), Boolean.class);
        if (value != null) {
            dataSource.setTestWhileIdle(value);
        }

        value = environment.getProperty(String.format(prefix, "testOnBorrow"), Boolean.class);
        if (value != null) {
            dataSource.setTestOnBorrow(value);
        }

        String property = environment.getProperty(String.format(prefix, "validationQuery"));
        if (property != null && property.length() > 0) {
            dataSource.setValidationQuery(property);
        }

        value = environment.getProperty(String.format(prefix, "useGlobalDataSourceStat"), Boolean.class);
        if (value != null) {
            dataSource.setUseGlobalDataSourceStat(value);
        }

        value = environment.getProperty(String.format(prefix, "useGloalDataSourceStat"), Boolean.class);
        if (value != null) {
            dataSource.setUseGlobalDataSourceStat(value);
        }

        value = environment.getProperty(String.format(prefix, "asyncInit"), Boolean.class);
        if (value != null) {
            dataSource.setAsyncInit(value);
        }

        property = environment.getProperty(String.format(prefix, "filters"));
        if (property != null && property.length() > 0) {
            try {
                dataSource.setFilters(property);
            } catch (SQLException var21) {
                logger.error("setFilters error", var21);
            }
        }

        property = environment.getProperty(String.format(prefix, "timeBetweenLogStatsMillis"));
        long l;
        if (property != null && property.length() > 0) {
            try {
                l = Long.parseLong(property);
                dataSource.setTimeBetweenLogStatsMillis(l);
            } catch (NumberFormatException var20) {
                logger.error("illegal property 'timeBetweenLogStatsMillis'", var20);
            }
        }

        property = environment.getProperty(String.format(prefix, "stat.sql.MaxSize"));
        int i;
        if (property != null && property.length() > 0) {
            try {
                i = Integer.parseInt(property);
                if (dataSource.getDataSourceStat() != null) {
                    dataSource.getDataSourceStat().setMaxSqlSize(i);
                }
            } catch (NumberFormatException var19) {
                logger.error("illegal property 'stat.sql.MaxSize'", var19);
            }
        }

        value = environment.getProperty(String.format(prefix, "clearFiltersEnable"), Boolean.class);
        if (value != null) {
            dataSource.setClearFiltersEnable(value);
        }

        value = environment.getProperty(String.format(prefix, "resetStatEnable"), Boolean.class);
        if (value != null) {
            dataSource.setResetStatEnable(value);
        }

        property = environment.getProperty(String.format(prefix, "notFullTimeoutRetryCount"));
        if (property != null && property.length() > 0) {
            try {
                i = Integer.parseInt(property);
                dataSource.setNotFullTimeoutRetryCount(i);
            } catch (NumberFormatException var18) {
                logger.error("illegal property 'notFullTimeoutRetryCount'", var18);
            }
        }

        property = environment.getProperty(String.format(prefix, "timeBetweenEvictionRunsMillis"));
        if (property != null && property.length() > 0) {
            try {
                l = Long.parseLong(property);
                dataSource.setTimeBetweenEvictionRunsMillis(l);
            } catch (NumberFormatException var17) {
                logger.error("illegal property 'timeBetweenEvictionRunsMillis'", var17);
            }
        }

        property = environment.getProperty(String.format(prefix, "maxWaitThreadCount"));
        if (property != null && property.length() > 0) {
            try {
                i = Integer.parseInt(property);
                dataSource.setMaxWaitThreadCount(i);
            } catch (NumberFormatException var16) {
                logger.error("illegal property 'maxWaitThreadCount'", var16);
            }
        }

        property = environment.getProperty(String.format(prefix, "maxWait"));
        if (property != null && property.length() > 0) {
            try {
                i = Integer.parseInt(property);
                dataSource.setMaxWait(i);
            } catch (NumberFormatException var15) {
                logger.error("illegal property 'maxWait'", var15);
            }
        }

        value = environment.getProperty(String.format(prefix, "failFast"), Boolean.class);
        if (value != null) {
            dataSource.setFailFast(value);
        }

        property = environment.getProperty(String.format(prefix, "phyTimeoutMillis"));
        if (property != null && property.length() > 0) {
            try {
                l = Long.parseLong(property);
                dataSource.setPhyTimeoutMillis(l);
            } catch (NumberFormatException var14) {
                logger.error("illegal property 'phyTimeoutMillis'", var14);
            }
        }

        property = environment.getProperty(String.format(prefix, "phyMaxUseCount"));
        if (property != null && property.length() > 0) {
            try {
                l = Long.parseLong(property);
                dataSource.setPhyMaxUseCount(l);
            } catch (NumberFormatException var13) {
                logger.error("illegal property 'phyMaxUseCount'", var13);
            }
        }

        property = environment.getProperty(String.format(prefix, "minEvictableIdleTimeMillis"));
        if (property != null && property.length() > 0) {
            try {
                l = Long.parseLong(property);
                dataSource.setMinEvictableIdleTimeMillis(l);
            } catch (NumberFormatException var12) {
                logger.error("illegal property 'minEvictableIdleTimeMillis'", var12);
            }
        }

        property = environment.getProperty(String.format(prefix, "maxEvictableIdleTimeMillis"));
        if (property != null && property.length() > 0) {
            try {
                l = Long.parseLong(property);
                dataSource.setMaxEvictableIdleTimeMillis(l);
            } catch (NumberFormatException var11) {
                logger.error("illegal property 'maxEvictableIdleTimeMillis'", var11);
            }
        }

        value = environment.getProperty(String.format(prefix, "keepAlive"), Boolean.class);
        if (value != null) {
            dataSource.setKeepAlive(value);
        }

        property = environment.getProperty(String.format(prefix, "keepAliveBetweenTimeMillis"));
        if (property != null && property.length() > 0) {
            try {
                l = Long.parseLong(property);
                dataSource.setKeepAliveBetweenTimeMillis(l);
            } catch (NumberFormatException var10) {
                logger.error("illegal property 'keepAliveBetweenTimeMillis'", var10);
            }
        }

        value = environment.getProperty(String.format(prefix, "poolPreparedStatements"), Boolean.class);
        if (value != null) {
            dataSource.setPoolPreparedStatements(value);
        }

        value = environment.getProperty(String.format(prefix, "initVariants"), Boolean.class);
        if (value != null) {
            dataSource.setInitVariants(value);
        }

        value = environment.getProperty(String.format(prefix, "initGlobalVariants"), Boolean.class);
        if (value != null) {
            dataSource.setInitGlobalVariants(value);
        }

        value = environment.getProperty(String.format(prefix, "useUnfairLock"), Boolean.class);
        if (value != null) {
            dataSource.setUseUnfairLock(value);
        }

        property = environment.getProperty(String.format(prefix, "driverClassName"));
        if (property != null) {
            dataSource.setDriverClassName(property);
        }

        property = environment.getProperty(String.format(prefix, "initialSize"));
        if (property != null && property.length() > 0) {
            try {
                i = Integer.parseInt(property);
                dataSource.setInitialSize(i);
            } catch (NumberFormatException var9) {
                logger.error("illegal property 'initialSize'", var9);
            }
        }

        property = environment.getProperty(String.format(prefix, "minIdle"));
        if (property != null && property.length() > 0) {
            try {
                i = Integer.parseInt(property);
                dataSource.setMinIdle(i);
            } catch (NumberFormatException var8) {
                logger.error("illegal property 'minIdle'", var8);
            }
        }

        property = environment.getProperty(String.format(prefix, "maxActive"));
        if (property != null && property.length() > 0) {
            try {
                i = Integer.parseInt(property);
                dataSource.setMaxActive(i);
            } catch (NumberFormatException var7) {
                logger.error("illegal property 'maxActive'", var7);
            }
        }

        value = environment.getProperty(String.format(prefix, "killWhenSocketReadTimeout"), Boolean.class);
        if (value != null) {
            dataSource.setKillWhenSocketReadTimeout(value);
        }

        property = environment.getProperty(String.format(prefix, "connectProperties"));
        if (property != null) {
            dataSource.setConnectionProperties(property);
        }

        property = environment.getProperty(String.format(prefix, "maxPoolPreparedStatementPerConnectionSize"));
        if (property != null && property.length() > 0) {
            try {
                i = Integer.parseInt(property);
                dataSource.setMaxPoolPreparedStatementPerConnectionSize(i);
            } catch (NumberFormatException var6) {
                logger.error("illegal property 'maxPoolPreparedStatementPerConnectionSize'", var6);
            }
        }

        property = environment.getProperty(String.format(prefix, "initConnectionSqls"));
        if (property != null && property.length() > 0) {
            try {
                StringTokenizer tokenizer = new StringTokenizer(property, ";");
                dataSource.setConnectionInitSqls(Collections.list(tokenizer));
            } catch (NumberFormatException var5) {
                logger.error("illegal property 'initConnectionSqls'", var5);
            }
        }

    }

}